#!/usr/bin/env python
# coding:utf-8
from PoboAPI import *
import datetime
import math
from copy import *

# 从期权列表中移除A期权
def RemoveAoptionInList(oplist):
    del_list = []
    for i in range(len(oplist)):
        op = oplist[i]
        info = GetContractInfo(op)
        if info['合约乘数'] != 10000:
            del_list.append(i)
    del_list.sort(reverse = True)
    for i in del_list:
        del oplist[i]
    return oplist
        

#Python3Previe w
#开始时间，用于初始化一些参数
def OnStart(context) :
    print("I\'m starting...")
    #设置全局变量，作为是否已经平仓的标志
    g.close=True
    #设置开仓阈值
    g.threshold=1.5
    #获取当前期权的到期日列表
    last= GetOptionsLastDates('510050.SHSE')
    #获取特定到期日的期权列表
    call_oplist = GetOptionContracts('510050.SHSE',last[-1],0)
    put_oplist=GetOptionContracts('510050.SHSE',last[-1],1)
    call_oplist = RemoveAoptionInList(call_oplist)
    put_oplist = RemoveAoptionInList(put_oplist)
    g.code=[call_oplist[0],call_oplist[1],put_oplist[0],put_oplist[1]]
    print("当前套利期权组合初始化为"+str(g.code))
    SubscribeQuote(g.code[0])
    #登录期权账号
    if "回测期权" in context.accounts :
        print("登录交易账号 回测期权")
        if context.accounts["回测期权"].Login() :
            context.myacc = context.accounts["回测期权"]
            
def OnMarketQuotationInitialEx(context,exchange,daynight):
    if exchange ==  'SHSE' and g.close==True:
        last= GetOptionsLastDates('510050.SHSE')
        call_oplist = GetOptionContracts('510050.SHSE',last[-1],0)
        put_oplist=GetOptionContracts('510050.SHSE',last[-1],1)
        call_oplist = RemoveAoptionInList(call_oplist)
        put_oplist = RemoveAoptionInList(put_oplist)
        UnsubscribeQuote(g.code)
        g.code=[call_oplist[0],call_oplist[1],put_oplist[0],put_oplist[1]]
        SubscribeQuote(g.code[0])
        print("当前套利期权组合重置为"+str(g.code))
            
#实时行情事件
def OnQuote(context,code) :
    print("调用到OnQuote事件")
    #如果当前持仓已平仓，寻找当前可进行套利的一组期权
    if g.close==True:
        #获取当前期权的到期日列表
        last= GetOptionsLastDates('510050.SHSE')
        #获取特定到期日的期权列表
        call_oplist = GetOptionContracts('510050.SHSE',last[-1],0)
        put_oplist= GetOptionContracts('510050.SHSE',last[-1],1)
        call_oplist = RemoveAoptionInList(call_oplist)
        put_oplist = RemoveAoptionInList(put_oplist)
        #获取期权的基本信息列表
        info=[]
        for op in call_oplist:
            info.append(GetContractInfo(op))
        #获取期权的行权价列表
        K=[]
        for i in info:
            K.append(i['行权价格'])
        #获取看涨期权当前最新价列表
        C=[]
        for op in call_oplist:
            C.append(GetQuote(op).now)
        #获取看跌期权当前最新价列表
        P=[]
        for op in put_oplist:
            P.append(GetQuote(op).now)
        #获取期权行权日
        T=last[-1]
        #获取当前交易日
        t=GetCurrentTradingDate('SHFE')
        #计算当前日距离到期日的时间，并换算成以年为单位
        time=float((T-t).days)/365.0
        #无风险利率设为0.04
        r=0.04
        code_list=[]
        spreads=[]
        #将列表可进行套利的期权加入code_list中，并将套利差加入spreads中
        for i in range(len(K)):
            for j in range(i+1,len(K)):
                K_all=(K[j]-K[i])*math.exp(-r*time)
                C_all=C[i]-C[j]
                P_all=P[j]-P[i]
                if C_all+P_all>= g.threshold*K_all or C_all+P_all<=-g.threshold*K_all:
                    code_list.append([call_oplist[i],call_oplist[j],put_oplist[i],put_oplist[j]])
                    spreads.append(abs(C_all+P_all))
        #如果code_list不为空，将套利差绝对值最大的期权组合保存到g.code中
        if code_list:
            UnsubscribeQuote(g.code[0])
            index=spreads.index(max(spreads))
            g.code=code_list[index]
            #print "套利期权组合更改为"+str(g.code)
            SubscribeQuote(g.code[0])
    #print "当前套利期权组合为"+str(g.code)
    #获取套利期权组合的基本信息
    info1=GetContractInfo(g.code[0])
    info2=GetContractInfo(g.code[1])
    #获取套利期权组合的行权价
    K1=info1['行权价格']
    K2=info2['行权价格']
    #获取套利期权组合的最新价
    dyndata1=GetQuote(g.code[0])
    dyndata2=GetQuote(g.code[1])
    C1=dyndata1.now
    C2=dyndata2.now
    dyndata3=GetQuote(g.code[2])
    dyndata4=GetQuote(g.code[3])
    P1=dyndata3.now
    P2=dyndata4.now
    #获取期权组合的到期日并转换为date格式
    T_int=info1['行权到期日']
    T_str=str(T_int)
    year=int(T_str[0:4])
    month=int(T_str[5:7])
    day=int(T_str[8:])
    T=datetime.date(year,month,day)
    #获取当前交易日
    t=GetCurrentTradingDate('SHFE')
    #计算当前日距离到期日的时间，并换算成以年为单位
    time=float((T-t).days)/365.0
    #无风险利率设为0.04
    r=0.04
    #计算K
    K=(K2-K1)*math.exp(-r*time)
    #计算C
    C=C1-C2
    #计算P
    P=P2-P1
    #获取期权组合的当前涨停价
    dyndata1=GetQuote(g.code[0])
    riselimit1=dyndata1.riselimit
    dyndata2=GetQuote(g.code[1])
    riselimit2=dyndata2.riselimit
    dyndata3=GetQuote(g.code[2])
    riselimit3=dyndata3.riselimit
    dyndata4=GetQuote(g.code[3])
    riselimit4=dyndata4.riselimit
    #获取期权组合的当前成交量
    trade1=dyndata1.volume
    trade2=dyndata2.volume
    trade3=dyndata3.volume
    trade4=dyndata4.volume
    price_type= PriceType(PbPriceType.Limit,limit_price_type=2,limit_price_offset=10)
    buy_price_type = PriceType(PbPriceType.Limit,limit_price_type=5)
    sell_price_type = PriceType(PbPriceType.Limit,limit_price_type=6)
    #若当前期权组合均可交易
    if trade1!=0 and trade2!=0 and trade3!=0 and trade4!=0:
        value = 0.8
        context.myacc.SetRiskDegreeMaxThreshold(value)
        isOver = context.myacc.IsRiskDegreeOverMax()
        if C+P>= g.threshold*K:
            if g.close==True:
                if isOver==False:
                #计算最佳下单量
                    v1=context.myacc.GetValidVolume(g.code[0],BSType.SellOpen,riselimit1)
                    v2=context.myacc.GetValidVolume(g.code[1],BSType.BuyOpen,riselimit2)
                    v3=context.myacc.GetValidVolume(g.code[2],BSType.BuyOpen,riselimit3)
                    v4=context.myacc.GetValidVolume(g.code[3],BSType.SellOpen,riselimit4)
                    print(v1,v2,v3,v4)
#                     v=int(math.ceil(min(v1,v2,v3,v4)/10))
                    v = 1
                #卖出低执行价看涨期权
                    QuickInsertOrder(context.myacc,g.code[0],'sell','open',price_type,v)
                #买入高执行价看涨期权
                    QuickInsertOrder(context.myacc,g.code[1],'buy','open',price_type,v)
                #买入低执行价看跌期权
                    QuickInsertOrder(context.myacc,g.code[2],'buy','open',price_type,v)
                #卖出高执行价看跌期权
                    QuickInsertOrder(context.myacc,g.code[3],'sell','open',price_type,v)
                    print("开仓成功")
                    g.close=False
        elif C+P<=-g.threshold*K:
            if g.close==True:
                if isOver==False:
                #计算最佳下单量
                    v1=context.myacc.GetValidVolume(g.code[0],BSType.BuyOpen,riselimit1)
                    v2=context.myacc.GetValidVolume(g.code[1],BSType.SellOpen,riselimit2)
                    v3=context.myacc.GetValidVolume(g.code[2],BSType.SellOpen,riselimit3)
                    v4=context.myacc.GetValidVolume(g.code[3],BSType.BuyOpen,riselimit4)
#                     v=int(math.ceil(min(v1,v2,v3,v4)/10))
                    v = 1
                #买入低执行价看涨期权
                    QuickInsertOrder(context.myacc,g.code[0],'buy','open',price_type,v)
                #卖出高执行价看涨期权
                    QuickInsertOrder(context.myacc,g.code[1],'sell','open',price_type,v)    
                #卖出低执行价看跌期权
                    QuickInsertOrder(context.myacc,g.code[2],'sell','open',price_type,v)    
                #买入高执行价看跌期权
                    QuickInsertOrder(context.myacc,g.code[3],'buy','open',price_type,v)    
                    print("开仓成功")
                    g.close=False
            #当C+P的值在g.threshold*K和-g.threshold*K之间，平掉所有持仓
        else:
            for c in g.code:
                option = PBObj()
                option.contract = c
                pos = context.myacc.GetPositions(option)
                for p in pos:
                    if p.bstype.BuySellFlag=="0":
                        QuickInsertOrder(context.myacc,c,'sell','close',price_type,p.volume)
                    if p.bstype.BuySellFlag=="1":
                        QuickInsertOrder(context.myacc,c,'buy','close',price_type,p.volume)
            g.close=True
    #若g.code中期权已到期
    if T==t:
        #平仓掉g.code中的持仓
        for c in g.code:
            option = PBObj()
            option.contract = c
            pos = context.myacc.GetPositions(option)
            for p in pos:
                if p.bstype.BuySellFlag=="0":
                    QuickInsertOrder(context.myacc,c,'sell','close',sell_price_type,p.volume)
                if p.bstype.BuySellFlag=="1":
                    QuickInsertOrder(context.myacc,c,'buy','close',buy_price_type,p.volume)
                print("到期强制平仓")
        #重新初始化g.code中的交易资产
        last= GetOptionsLastDates('510050.SHSE')
        call_oplist = GetOptionContracts('510050.SHSE',last[-1],0)
        put_oplist=GetOptionContracts('510050.SHSE',last[-1],1)
        UnsubscribeQuote(g.code[0])
        g.code=[call_oplist[0],call_oplist[1],put_oplist[0],put_oplist[1]]
        #print "当前套利期权已到期,重新初始化为"+str(g.code)
        SubscribeQuote(g.code[0])
        g.close=True
        